﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Pifagor.IG;
using Pifagor.Exceptions;

namespace Pifagor.Runtime
{
	public class Value : IComputableValue
	{
		#region Fields
		Value _functionArgumentValue;
		#endregion

		#region Public properties
		public ValueType Type { get; private set; }

		public DataList DataListValue { get; private set; }

		public ParList ParListValue { get; private set; }

		public int IntegerValue { get; private set; }

		public InternalFunction InternalFunctionValue { get; private set; }

		public ExternalFunction ExternalFunctionValue { get; private set; }
		#endregion

		#region Ctors
		internal Value(ParList list)
		{
			ParListValue = list;
			Type = ValueType.ParList;
		}

		internal Value(DataList list)
		{
			DataListValue = list;
			Type = ValueType.DataList;
		}

		internal Value(int value)
		{
			IntegerValue = value;
			Type = ValueType.Integer;
		}

		internal Value(ExecutionContext context, IGConstant constant)
		{
			switch (constant.Type)
			{
				case IGConstantType.Integer:
					IntegerValue = constant.IntegerValue;
					Type = ValueType.Integer;
					break;

				case IGConstantType.InternalFunction:
					InternalFunctionValue = new InternalFunction(constant);
					Type = ValueType.InternalFunction;
					break;

				case IGConstantType.ExternalFunction:
					string externalFunctionName = context.IG.Functions[constant.ExternalFunctionId].Name;
					ExternalFunctionValue = new ExternalFunction(
						context.SetIG(context.Interpreter.GetIG4Function(externalFunctionName))
						);
					Type = ValueType.ExternalFunction;
					break;

				case IGConstantType.FunctionArgument:
					if (context.FunctionArgument == null)
						throw new InvalidOperationException("Function argument is not specified for execution context");
					_functionArgumentValue = context.FunctionArgument.Compute();
					Type = _functionArgumentValue.Type;
					break;

				default:
					throw new RuntimeException("Unrecognized constant type");
			}
		}
		#endregion

		#region IComputableValue members
		public Value Compute()
		{
			if (_functionArgumentValue != null)
				return _functionArgumentValue;
			return this;
		}
		#endregion

		#region Public methods
		public override string ToString()
		{
			switch (Type)
			{ 
				case ValueType.DataList:
					return DataListValue.ToString();

				case ValueType.ParList:
					return ParListValue.ToString();

				case ValueType.Integer:
					return IntegerValue.ToString();

				default:
					throw new RuntimeException("Value does not support ToString");
			}
		}
		#endregion
	}
}
